package tzhomestudio.framework.implementation.log_service;

import tzhomestudio.framework.interfaces.log_service.ILogger;
import tzhomestudio.framework.shared.utils.DateUtils;
import tzhomestudio.framework.shared.utils.FileUtils;
import tzhomestudio.framework.shared.utils.RandomUtils;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * This class realizes interface ILogger.
 * Messages logs to file.
 *
 * @author Timur Zolotuhin (e-mail: tzolotuhin@gmail.com)
 */
public class FileLogger implements ILogger {
    // Main data
    private File logFile;
    private DataOutputStream output;
    private final ConsoleLogger consoleLogger;

    private boolean enableDebugMode;

    // Mutex
    private final Object generalMutex = new Object();

    public FileLogger() {
        this(new File(FileUtils.getWorkingDir() + "log" + File.separator + DateUtils.getStartAppTimeInDefaultFormat() + "_" + RandomUtils.generateRandomUUID() + "_log.txt"));
    }

    public FileLogger(File logFile) {
		this.logFile = logFile;
        consoleLogger = new ConsoleLogger();

		// create log directory, if it isn't exist
        File logDir = logFile.getParentFile();
		if(!logDir.exists() && !logDir.mkdirs()) {
            throw new RuntimeException("Couldn't to create log directory " + logDir.getAbsolutePath());
		}

        // try open file for writing
        try {
            FileOutputStream fos = new FileOutputStream(logFile);
			output = new DataOutputStream(fos);
		} catch (IOException ex) {
			throw new RuntimeException("Couldn't to create file " + logFile);
		}
	}

    @Override
    public void enableDebugMessage(boolean enable) {
        synchronized (generalMutex) {
            enableDebugMode = enable;

            consoleLogger.enableDebugMessage(enable);
        }
    }

    @Override
    public void printErrorAndThrowRuntimeException(String message) throws RuntimeException {
        synchronized (generalMutex) {
            printError(message);
            throw new RuntimeException(message);
        }
    }

    @Override
	public void printDebug(String message) {
        synchronized (generalMutex) {
            if (!enableDebugMode) return;

            try {
                // print to console
                consoleLogger.printDebug(message);

                // print to file
                output.write(("[DEBUG] : " + message + "\n").getBytes());
                output.flush();
            } catch (IOException ex) {
                consoleLogger.printDebug(message);
            }
        }
	}

    @Override
    public void printDebug(String message, Throwable ex) {
        synchronized (generalMutex) {
            if (!enableDebugMode) return;

            printDebug(message);
            printException(ex);
        }
    }

    @Override
    public void printError(String message) {
        synchronized (generalMutex) {
            try {
                // print to console
                consoleLogger.printError(message);

                // print to file
                output.write(("[ERROR] : " + message + "\n").getBytes());
                output.flush();
            } catch (IOException ex) {
                consoleLogger.printError(message);
            }
        }
	}

    @Override
    public void printError(String message, Throwable ex) {
        synchronized (generalMutex) {
            printError(message);
            printException(ex);
        }

    }

    @Override
    public void printInfo(String message) {
        synchronized (generalMutex) {
            try {
                // print to console
                consoleLogger.printInfo(message);

                // print to file
                output.write(("[INFO] : " + message + "\n").getBytes());
                output.flush();
            } catch (IOException ex) {
                consoleLogger.printInfo(message);
            }
        }
	}

    @Override
    public void printException(Throwable exception) {
        synchronized (generalMutex) {
            if (exception == null) return;

            try {
                output.write(("[TRACE START] [" + exception.getMessage() + "]\n").getBytes());
                for (StackTraceElement element : exception.getStackTrace()) {
                    output.write(("[TRACE] : " + element.toString() + "\n").getBytes());
                }
                output.write("[TRACE END]\n".getBytes());
            } catch (IOException ex) {
                consoleLogger.printException(ex);
            } finally {
                consoleLogger.printException(exception);
            }
        }
	}

    @Override
    public String getLoggerInfo() {
        synchronized (generalMutex) {
            return "Path to log file: " + logFile;
        }
	}
}
